Controlador AVR para reloj hecho con led matrix de 8x8
El circuito del controlador en Kicad es el siguiente: Clock controller
El circuito del led matrix en Kicad es el siguiente: Led Matrix 74HC595
#include <avr/io.h>
#include <avr/power.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include "digits.h"
#define LATCH_PIN PB2 // ST_CP
#define DISPLAY_COLUMNS 8
#define DISPLAY_ROWS 8
#define CHAR_COLUMNS 8
#define NUMBER_OF_DISPLAYS 1
#define SLIDE_SPEED_DELAY 60
#define TIME_CALIBRATION -4
volatile uint16_t time_counter = 0;
volatile uint16_t time2_counter = 0;
volatile uint8_t display[NUMBER_OF_DISPLAYS][DISPLAY_ROWS];
volatile uint8_t hour_1;
volatile uint8_t hour_2;
volatile uint8_t hour_1;
volatile uint8_t min_1;
volatile uint8_t min_2;
volatile uint8_t min_1_animation = 0;
volatile uint8_t min_2_animation = 0;
volatile uint8_t hour_1_animation = 0;
volatile uint8_t hour_2_animation = 0;
volatile uint8_t animation_counter = 0;
volatile uint8_t display_hour_1[DISPLAY_ROWS];
volatile uint8_t display_hour_2[DISPLAY_ROWS];
volatile uint8_t display_min_1[DISPLAY_ROWS];
volatile uint8_t display_min_2[DISPLAY_ROWS];
volatile uint8_t display_seconds[DISPLAY_ROWS];
volatile uint8_t seconds=0;
volatile uint8_t minutes = 0;
volatile uint8_t hours = 0;
volatile uint8_t count = 0;
void SPI_init(void) {
// Configurar MOSI, SCK y LATCH como salida
DDRB |= (1 << PB3) | (1 << PB5) | (1 << LATCH_PIN);
// Configurar SPI como Master
SPCR = (1 << SPE) | (1 << MSTR);
}
void SPI_send(uint8_t data) {
SPDR = data; // Cargar dato en el registro de SPI
while (!(SPSR & (1 << SPIF))); // Esperar que termine la transmisión
}
void latch() {
// Pulso en LATCH para actualizar salidas
PORTB |= (1 << LATCH_PIN);
//_delay_us(1); // Opcional? descomentar en caso de que el latch no funcione
PORTB &= ~(1 << LATCH_PIN);
}
void increase_seconds(){
display_seconds[seconds/DISPLAY_ROWS] |= (1 << (seconds%8));
seconds++;
}
ISR(TIMER0_COMPA_vect) {
SPI_send(display_hour_1[count]);
SPI_send(display_hour_2[count]);
SPI_send(display_min_1[count]);
SPI_send(display_min_2[count]);
SPI_send(display_seconds[count]);
SPI_send(~(1 << count));
latch();
count++;
if(count >= DISPLAY_ROWS)
count=0;
}
ISR(TIMER1_COMPA_vect) {
time_counter += 64;
if(time_counter >= 1000){
time_counter -= 1000;
increase_seconds();
if(seconds >= 60){
seconds = 0;
for(uint8_t i = 0; i < 8; i++)
display_seconds[i] = 0;
minutes++;
min_2 = minutes%10;
min_1 = (minutes/10)%10;
min_2_animation = 1;
if(minutes%10 == 0)
min_1_animation = 1;
}
if(minutes >= 60){
minutes = 0;
min_2 = minutes%10;
min_1 = (minutes/10)%10;
hours++;
hour_2 = hours%10;
hour_1 = (hours/10)%10;
hour_2_animation = 1;
if(hours%10 == 0)
hour_1_animation = 1;
}
if(hours >= 24){
hour_2 = 0;
hour_1 = 0;
hours = 0;
hour_1_animation = 1;
}
}
}
void update_animation(volatile uint8_t *display, uint8_t number){
for(uint8_t i = 0; i < 7; i++)
display[i] = display[i+1];
display[7] = digits[number][animation_counter];
}
void disable_animations(){
hour_1_animation = 0;
hour_2_animation = 0;
min_1_animation = 0;
min_2_animation = 0;
}
ISR(TIMER2_COMPA_vect) {
time2_counter++;
if(time2_counter >= 3){
time2_counter = 0;
if(min_2_animation){
update_animation(display_min_2, min_2);
}
if(min_1_animation){
update_animation(display_min_1, min_1);
}
if(hour_2_animation){
update_animation(display_hour_2, hour_2);
}
if(hour_1_animation){
update_animation(display_hour_1, hour_1);
}
if(min_1_animation || min_2_animation){
animation_counter++;
}
if(animation_counter == 8){
animation_counter = 0;
disable_animations();
}
}
}
void init_timer0(){
TCCR0A |= (1 << WGM01);
TCCR0B |= (1 << CS02); // Prescaler de 256
TIMSK0 |= (1 << OCIE0A);
OCR0A = 2; // Interrupción cada 512 microsegundos
}
void init_timer1() {
TCCR1B |= (1 << WGM12); // Modo CTC
OCR1A = 64000 + TIME_CALIBRATION;
TCCR1B |= (1 << CS11); // Prescaler 8
TIMSK1 |= (1 << OCIE1A); // Habilitar interrupción por OCR1A
}
void init_timer2(){
TCCR2A |= (1 << WGM21); // modo CTC
TCCR2B |= (1 << CS22) | (1 << CS21) | (1 << CS20); // Clock with /1024 prescaler
TIMSK2 |= (1 << OCIE2A); // Habilitar interrupción por OCR2A
OCR2A = 250;
}
void set_zero_time(volatile uint8_t *display){
for(uint8_t i = 0; i < 8; i++)
display[i] = digits[0][i];
}
ISR(PCINT1_vect) {
if(bit_is_clear(PINC, PC0)){
seconds = 0;
time_counter = 0;
for(uint8_t i = 0; i < 8; i++)
display_seconds[i] = 0;
TCNT1 = 0;
}
else if(bit_is_clear(PINC, PC1)){
minutes++;
if(minutes >= 60)
minutes = 0;
min_2 = minutes%10;
min_1 = (minutes/10)%10;
for(uint8_t i = 0; i < 8; i++){
display_min_1[i] = digits[min_1][i];
display_min_2[i] = digits[min_2][i];
}
}
else if(bit_is_clear(PINC, PC2)){
hours++;
if(hours >= 24)
hours = 0;
hour_2 = hours%10;
hour_1 = (hours/10)%10;
for(uint8_t i = 0; i < 8; i++){
display_hour_1[i] = digits[hour_1][i];
display_hour_2[i] = digits[hour_2][i];
}
}
_delay_ms(4);
PCIFR |= (1 << PCIF1);
}
void init_button_interrupts(){
PCICR |= (1 << PCIE1);
PCMSK1 |= (1 << PCINT8) | (1 << PCINT9) | (1 << PCINT10);
}
int main(void) {
clock_prescale_set(clock_div_1);
SPI_init();
PORTC = 0b00000111;
init_timer0();
init_timer1();
init_timer2();
init_button_interrupts();
sei();
set_zero_time(display_min_1);
set_zero_time(display_min_2);
set_zero_time(display_hour_1);
set_zero_time(display_hour_2);
while (1) {
}
}
digits.h file:
/* digits_h_inverted.h - 0..9, cada número 8 bytes, LSB = columna izquierda */
#include <stdint.h>
volatile uint8_t digits[][8] =
{
// 0
{
0b00111100,
0b01000010,
0b01100010,
0b01010010,
0b01001010,
0b01000110,
0b00111100,
0b00000000,
},
// 1
{
0b00010000,
0b00011000,
0b00010100,
0b00010000,
0b00010000,
0b00010000,
0b00111100,
0b00000000,
},
// 2
{
0b00111100,
0b01000010,
0b01000000,
0b00110000,
0b00001000,
0b00000100,
0b01111110,
0b00000000,
},
// 3
{
0b00111100,
0b01000010,
0b01000000,
0b00111000,
0b01000000,
0b01000010,
0b00111100,
0b00000000,
},
// 4
{
0b00100000,
0b00110000,
0b00101000,
0b00100100,
0b01111110,
0b00100000,
0b00100000,
0b00000000,
},
// 5
{
0b01111110,
0b00000010,
0b00111110,
0b01000000,
0b01000000,
0b01000010,
0b00111100,
0b00000000,
},
// 6
{
0b00111100,
0b01000010,
0b00000010,
0b00111110,
0b01000010,
0b01000010,
0b00111100,
0b00000000,
},
// 7
{
0b01111110,
0b01000000,
0b00100000,
0b00010000,
0b00001000,
0b00001000,
0b00001000,
0b00000000,
},
// 8
{
0b00111100,
0b01000010,
0b01000010,
0b00111100,
0b01000010,
0b01000010,
0b00111100,
0b00000000,
},
// 9
{
0b00111100,
0b01000010,
0b01000010,
0b01111100,
0b01000000,
0b01000010,
0b00111100,
0b00000000,
},
};
Circuito simulIDE: Clock controller
AVR | microcontrolador | clock | controller | led matrix